package com.lizhi.note.controller;

import com.lizhi.note.config.JwtUtils;
import com.lizhi.note.dto.LoginRequest;
import com.lizhi.note.dto.LoginSuccessResponse;
import com.lizhi.note.payload.ApiResponse;
import com.lizhi.note.payload.ResponseUtil;
import com.lizhi.note.repository.AppUserRepository;
import com.lizhi.note.security.RsaKeyGenerator;
import com.lizhi.note.service.TokenBlackListCacheService;
import jakarta.servlet.http.Cookie;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import jakarta.validation.Valid;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.http.ResponseEntity;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.core.userdetails.User;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.util.StringUtils;
import org.springframework.web.bind.annotation.*;

import javax.crypto.Cipher;
import java.net.URI;
import java.net.URISyntaxException;
import java.security.KeyPair;
import java.security.PrivateKey;
import java.util.Base64;
import java.util.HashMap;
import java.util.Map;
import java.util.UUID;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;

@CrossOrigin(origins = "*", maxAge = 3600)
@RestController
@RequestMapping("/api/auth")
public class AuthController {

    private final ConcurrentMap<String, PrivateKey> privateKeyCache = new ConcurrentHashMap<>();

    @Autowired
    TokenBlackListCacheService tokenBlackListCacheService;

    @Autowired
    AuthenticationManager authenticationManager;

    @Autowired
    JwtUtils jwtUtils;

    @Value("${app.allowed.origins}")
    private String[] allowedOrigins;

    @GetMapping("/public-key")
    public ResponseEntity<?> getPublicKey() {
        try {
            KeyPair keyPair = RsaKeyGenerator.generateKeyPair();
            String publicKey = RsaKeyGenerator.encodePublicKey(keyPair.getPublic());
            String requestId = UUID.randomUUID().toString() + "_" + System.currentTimeMillis();

            privateKeyCache.put(requestId, keyPair.getPrivate());

            Map<String, String> result = new HashMap<>();
            result.put("publicKey", publicKey);
            result.put("requestId", requestId);
            result.put("timestamp", String.valueOf(System.currentTimeMillis()));
            return ResponseUtil.success(result, "获取公钥成功");
        } catch (Exception e) {
            throw new RuntimeException("Failed to generate public key", e);
        }
    }

    @PostMapping("/login")
    public ResponseEntity<?> authenticateUser(@Valid @RequestBody LoginRequest loginRequest, HttpServletResponse response) throws Exception {

        String[] parts = loginRequest.getRequestId().split("_");
        if (parts.length != 2) {
            throw new IllegalArgumentException("Invalid request ID");
        }

        long timestamp = Long.parseLong(parts[1]);
        if (System.currentTimeMillis() - timestamp > 5 * 60 * 1000) {
            throw new IllegalArgumentException("Request expired");
        }

        PrivateKey privateKey = privateKeyCache.remove(loginRequest.getRequestId());
        if (privateKey == null) {
            throw new IllegalArgumentException("Invalid or expired key");
        }

        String decryptedPassword = decryptRsa(loginRequest.getPassword(), privateKey);


        Authentication authentication = authenticationManager.authenticate(
                new UsernamePasswordAuthenticationToken(loginRequest.getUsername(), decryptedPassword));

        User user = (User) authentication.getPrincipal();
        String jwtToken = jwtUtils.generateToken(user);
        LoginSuccessResponse loginSuccessResponse = new LoginSuccessResponse(jwtToken, user.getUsername());

        for (String origin: this.allowedOrigins) {
            String domain = this.extractHost(origin);
            // 创建JWT Cookie
            Cookie jwtCookie = new Cookie("token", jwtToken);
            jwtCookie.setPath("/");                // 整个应用都可以访问
            jwtCookie.setHttpOnly(false);           // 防止XSS攻击
            jwtCookie.setSecure(false);             // 仅HTTPS传输（生产环境建议启用）
            jwtCookie.setMaxAge(60 * 60 * 24);     // 有效期1天（秒）
            jwtCookie.setDomain(domain);
            //jwtCookie.setAttribute("SameSite","None");
            // 添加Cookie到响应
            response.addCookie(jwtCookie);
        }

        return ResponseEntity.ok(ApiResponse.success(loginSuccessResponse));
    }

    @PostMapping("/logout")
    public ResponseEntity<?> logout(HttpServletRequest request, HttpServletResponse response){

        Cookie[] cookies = request.getCookies();
        String token = "invalid";
        for (Cookie cookie : cookies) {
            token = cookie.getAttribute("token");
            if (StringUtils.hasText(token)){
                tokenBlackListCacheService.putElement(token);
                break;
            }
        }

        String domain = request.getServerName();

        // 创建JWT Cookie
        Cookie jwtCookie = new Cookie("token", token);
        jwtCookie.setPath("/");                // 整个应用都可以访问
        jwtCookie.setHttpOnly(false);           // 防止XSS攻击
        jwtCookie.setSecure(false);             // 仅HTTPS传输（生产环境建议启用）
        jwtCookie.setMaxAge(1);     // 有效期1天（秒）
        jwtCookie.setDomain(domain);
        //jwtCookie.setAttribute("SameSite","None");
        // 添加Cookie到响应
        response.addCookie(jwtCookie);

        return ResponseUtil.success(null, "退出登录成功");
    }

    @GetMapping("/token")
    public ResponseEntity<?> validToken() {
        return ResponseEntity.ok(ApiResponse.success("验证通过"));
    }

    private String decryptRsa(String encryptedData, PrivateKey privateKey) throws Exception {
        Cipher cipher = Cipher.getInstance("RSA");
        cipher.init(Cipher.DECRYPT_MODE, privateKey);
        byte[] decryptedBytes = cipher.doFinal(Base64.getDecoder().decode(encryptedData));
        return new String(decryptedBytes);
    }

    // 定时清理过期密钥
    @Scheduled(fixedDelay = 60_000)
    public void cleanupExpiredKeys() {
        long fiveMinutesAgo = System.currentTimeMillis() - 5 * 60 * 1000;
        privateKeyCache.entrySet().removeIf(entry ->
                Long.parseLong(entry.getKey().split("_")[1]) < fiveMinutesAgo
        );
    }

    public String extractHost(String url){
        try {
            URI uri = new URI(url);
            return uri.getHost();
        } catch (URISyntaxException e) {
            e.printStackTrace();
            return null;
        }
    }
}
